<template>
    <div class="treeContainer" >
        <a-tree
        :show-line="showLine"
        :show-icon="showIcon"
        :default-expanded-keys="['0-0-0']"
        :tree-data="treeData"
        draggable
        blockNode
        @drop="onDrop"
        @dragstart="onDragstart"
        @dragover="onDragover"
        @dragend="onDragend"
        >
        </a-tree>
        <!--提示框的容器，方便后期删除提示框，使用v-if-->
        <div id="tooltipContainer" v-if="flag"></div>
    </div>
    <a-modal v-model:open="open" title="Basic Modal" @ok="handleOk">
      <p>是否{{ tooltip }}</p>
    </a-modal>
</template>

<script lang="ts" setup>
// import {ref,reactive} from 'vue'
import { ref,onMounted,createVNode   } from 'vue';
import type {
  AntTreeNodeDragEnterEvent,
  AntTreeNodeDropEvent,
  TreeProps,
} from 'ant-design-vue/es/tree';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import axios from "axios"
import { Modal } from 'ant-design-vue';
const showLine = ref<boolean>(true);
const showIcon = ref<boolean>(false);
let treeData = ref<TreeProps['treeData']>([]);
let tooltip=ref<string>('') //表示弹窗的操作内容
const open = ref<boolean>(false);
const init=()=>{
  axios.get("http://localhost:5173/api/data").then((res) => {
    console.log("这是我发送的请求");
    console.log(res)
    console.log(res.data.data)
    treeData.value=res.data.data;
  })
}
const handleOk = (e: MouseEvent) => {
  console.log(e);
  open.value = false;
};
onMounted(()=>{
  console.log('qqweer')
  init();
})

interface dragNode{
  title:string,
  key:string,
  children:[]
}
//递归查找元素的索引公共方法——查找拖拽元素和目标元素
const findNodeIndex=(data:TreeProps['treeData'],key:string,callback:any)=>{
  data?.forEach((item,index,arr)=>{
      //找到元素，并返回相关元素，索引，及其当前循环的数组即查找元素的父级数组
      if(item.key==key){
          console.log('找到了')
          return callback(item,index,arr)
      }
      if(item.children){
          return findNodeIndex(item.children,key,callback)
      }
  })
}

/**
 * 获取拖拽节点和目标节点的位置，生成提示
 */
let drag_pos=0;//拖拽元素的索引
let drag_arr:[]=[];//存储拖拽节点的父级层级数组,方便后期比较是否是同层级
let drag_allArr:[]=[]; //拖拽节点的全部层级数组，判断是否拖拽到与其另一个相同节点的子级
let drag_title='';//存储拖拽节点的名称
let drop_pos=0;//经过的节点的索引
let drop_title='';//存储目标节点的名称
let flag=ref<Boolean>(true) //判断tooltip元素容器的显示和隐藏

//拖拽开始，获取拖拽元素信息
const onDragstart=(node:any)=>{
  console.log('拖拽节点',node)
  let arr=node.node.pos.split('-')
  drag_allArr=arr;
  drag_arr=arr.slice(0,arr.length-1) //获取拖拽节点层级
  drag_pos=Number(arr[arr.length-1])
  drag_title=node.node.title; 
  //获取索引
  findNodeIndex(treeData.value,node.node.key,(item:dragNode,index:number,arr:[])=>{
    drag_pos=index;
  })
  flag.value=true; //默认显示tooltip元素，初始化
  console.log('drag_arr',drag_arr)
}
//拖拽过程中
const onDragover=(node:any)=>{
  console.log('拖拽经过',node)
  let arr1=node.node.pos.split('-')
  let drop_arr=arr1.slice(0,arr1.length-1) //获取目标节点层级
  let drop_parent_title=''
  drop_pos=Number(arr1[arr1.length-1])
  drop_title=node.node.title;
  //获取索引
  findNodeIndex(treeData.value,node.node.key,(item:dragNode,index:number,arr:[])=>{
    drop_pos=index;
  })
  /**
   * drag_pos<drop_pos 表示在目标元素下方插入
   * drag_pos>drop_pos 表示在目标元素上方插入
   */
  //判断是否是同级
  if(JSON.stringify(drag_arr)==JSON.stringify(drop_arr)){
    console.log('是同级',drag_pos,drop_pos)
    if((drag_pos<drop_pos) && (drag_title!==drop_title) ){
        // console.log('在'+node.node.title+'的下方插入');
        flag.value=true;//显示提示框
        let text='将放置在【'+node.node.title+'】的下方'
        tooltip.value='移动位置';//对话框显示操作文本
        createElementDom(node.event.clientX,node.event.clientY,text)
    }
    if((drag_pos>drop_pos) && (drag_title!==drop_title)){
        // console.log('在'+node.node.title+'的上方插入')
        flag.value=true; //显示提示框
        let text='将放置在【'+node.node.title+'】的上方'
        tooltip.value='移动位置';//对话框显示操作文本
        createElementDom(node.event.clientX,node.event.clientY,text)
    }
    if((drag_title==drop_title)){
        flag.value=true; //隐藏提示框
        console.log('重合')
        let text='将与【'+node.node.title+'】节点合并'
        tooltip.value='合并节点';//对话框显示操作文本
        createElementDom(node.event.clientX,node.event.clientY,text)
    }
  }else{
    console.log('不是同级,判断目标节点的父级是否是同一节点',drag_arr,drop_arr)
    // if(drag_allArr.length==drop_arr.length){
      console.log('拖拽节点在同层下的相同节点的子节点上，此时可合并')
      flag.value=true; //隐藏提示框
      console.log('重合')
      let text='将放置在【'+node.node.title+'】节点'
      tooltip.value='移动位置';//对话框显示操作文本
      createElementDom(node.event.clientX,node.event.clientY,text)
    // }

  }
}
//拖拽结束
const onDragend=()=>{
  console.log('拖拽结束')
  flag.value=false;
  console.log(flag.value)
}
//创建tooltip提示框
const createElementDom=(x:number,y:number,text:string)=>{
  let parentNode=document.getElementById('tooltipContainer');
  if(parentNode){
    (parentNode as HTMLElement).innerHTML='' //初始化，dragover每次触发时都清空之前创建的所有元素
  }
  const newElement = document.createElement('div');
  newElement.innerText = text;
  newElement.id='tooltip'
  newElement.style.position = 'absolute';
  newElement.style.left = `${x+30}px`; // 使用clientX和clientY定位元素
  newElement.style.top = `${y+30}px`;
  newElement.style.background='#409eff';
  newElement.style.padding='5px 15px';
  newElement.style.borderRadius='15px';
  newElement.style.color='#fff';
  if(parentNode){
    (parentNode as HTMLElement).appendChild(newElement);
  }
}

/**
 * 实现拖拽核心方法onDrop
 */
//拖拽
const onDrop = (nodeInfo: AntTreeNodeDropEvent) => {
  console.log(nodeInfo)
  const dragKey:string = (nodeInfo.dragNode.eventKey as string);//拖拽节点key
  const dropKey:string = (nodeInfo.node.eventKey as string); //目标节点key
  const dropPos=(nodeInfo.node.pos as string).split('-'); //获取目标节点的位置信息
  const dragPos=(nodeInfo.dragNode.pos as string).split('-'); //获取拖拽节点的位置信息
  let oldData=JSON.stringify(treeData.value)
  let data=JSON.parse(oldData)//复制一份数据，防止修改原始数据
  console.log(data)
  /**
   * dropPosition:
   * 0:  插入到目标节点内
   * 1： 插入到目标节点之后
   * -1：插入到目标节点之前
   */
  //计算偏移位置
  const dropPosition:number = nodeInfo.dropPosition - Number(dropPos[dropPos.length - 1]); 
  console.log('dropPosition',dropPosition);

  let dragArr:TreeProps['treeData'] =[]; //定义拖拽元素的父级数组。利用数组赋值时是用一个引用地址的原因，达到修改此数组，引用treeData的变化
  let dragIndex=0; //定义拖拽元素的索引
  let dragObj:any={
    title:'',
    key:'',
    children:[]
  }; //存储拖拽元素对象，方便后期替换和插入
  let dropArr:TreeProps['treeData']=[];//定义目标元素的父级数组。利用数组赋值时是用一个引用地址的原因，达到修改此数组，引用treeData的变化
  let dropIndex=0;//定义目标元素的索引
  let dropObj:any={
    title:'',
    key:'',
    children:[]
  }; //存储目标节点对象

  //查找拖拽元素,并删除拖拽节点
  findNodeIndex(data,dragKey,(item:dragNode,index:number,arr:[])=>{
      dragArr=arr;
      dragIndex=index;
      // 查找到后删除此元素
      arr.splice(index, 1);
      dragObj=item;
  })
  //查找目标元素
  findNodeIndex(data,dropKey,(item:dragNode,index:number,arr:[])=>{
      dropArr=arr;
      dropIndex=index;
      dropObj=item;
  })

  Modal.confirm({
    title: '提示',
    icon: createVNode(ExclamationCircleOutlined),
    content: createVNode('div', { style: 'color:red;' }, `是否${tooltip.value}`),
    onOk() {
      console.log('OK');
      //拖拽的不是节点的间隙，拖拽到出现上一级节点的位置下划线时
      if (!nodeInfo.dropToGap) {
        console.log('eeee')
        //有子节点的相同节点，即展开的相同节点
        if((dragArr as any).some((item:any)=>item.key==dropKey) && dragObj.title==dropObj.title){
          console.log('相同节点不可追加',dropArr,dropIndex,dragIndex)
          //等式成立表示从上往下拖拽合并
          if(nodeInfo.dropPosition==Number(dragPos[dragPos.length - 1])+1){
            if(dragObj.children.length>0){
              // console.log('gggbbb',dropArr[dropIndex].children);
              // 将所有的拖拽节点的子节点都追加到目标元素的children中
              for(let i=0;i<(dragObj.children as []).length;i++){
                // console.log(dragObj.children[i]);
                (dropArr as any)[dropIndex].children?.push(dragObj.children[i])
              }
              console.log((dropArr as any)[dropIndex].children)
              open.value = true; //显示对话框
              tooltip.value='合并'
            }else{

            }
          }else if(nodeInfo.dropPosition==Number(dragPos[dragPos.length - 1])-1){
            //等式成立表示从下往上拖拽合并
            if(dragObj.children.length>0){
              console.log('gggbbb',(dropArr as any)[dropIndex].children);
              // 将所有的拖拽节点的子节点都追加到目标元素的children中
              for(let i=0;i<(dragObj.children as []).length;i++){
                // console.log(dragObj.children[i]);
                (dropArr as any)[dropIndex].children?.push(dragObj.children[i])
              }
              console.log((dropArr as any)[dropIndex].children)
            }else{
              
            }
          }
        }else{
          console.log('其他节点情况')
          // if(dragArr.some(item=>item.key==dropKey) && dragObj.title!==dropObj.title){
          //   console.log('同层级，跨级节点不可拖拽')
          //   return false;
          // }
          // return false;
          findNodeIndex(data,dropKey,(item:dragNode,index:number,arr:[])=>{
              item.children=item.children || [];
              (item.children as any).unshift(dragObj)
          })
        }
      
        console.log(dropArr)
      }else if( (nodeInfo.node.children || []).length>0 && nodeInfo.node.expanded && dropPosition==1){
        //目标节点存在子节点，且是展开，且想插入到目标节点的子节点开头
        // return false;
        console.log('jjjjj')
        findNodeIndex(data,dropKey,(item:dragNode,index:number,arr:[])=>{
            item.children=item.children || [];
            (item.children as any).unshift(dragObj);
        })
      }else{
        console.log('aaaa')

        //在同级中才能拖拽或者合并
        if((dragArr as any).some((item:any)=>item.key==dropKey)){
          console.log('同级');

          //相同层级且名称一样的节点且不展开，可合并
          if(dragObj.title==dropObj.title){
            if(dragObj.children.length>0){
              console.log('gggbbb',(dropArr as any)[dropIndex].children);
              // 将所有的拖拽节点的子节点都追加到目标元素的children中
              for(let i=0;i<(dragObj.children as []).length;i++){
                // console.log(dragObj.children[i]);
                (dropArr as any)[dropIndex].children?.push(dragObj.children[i])
              }
              console.log((dropArr as any)[dropIndex].children)
            }else{
              
            }
          }else{
            console.log('fff');
            //不相同层级节点，只能拖拽。拖拽元素和目标元素替换
            tooltip.value='移动'
            if (dropPosition === -1) {
              (dropArr as any).splice(dropIndex,0,dragObj) //在目标节点前插入
              // resolve()
            } else {
              (dropArr as any).splice(dropIndex+1,0,dragObj) //在目标节点后插入
            }
          
          
          }
        }else{
          //处理不同级时，其节点父级同级，可实现合并
          console.log('wwww','非同级')
          let arr=(nodeInfo.dragNode.pos as string).split('-');
          let dragIndexArr=arr.slice(0,arr.length-1)
          let arr1=(nodeInfo.node.pos as string).split('-');
          let dropIndexArr=arr1.slice(0,arr1.length-1)
          console.log(dragIndexArr,dropIndexArr);
          //判断其父级是否是同级
          if(JSON.stringify(dragIndexArr)==JSON.stringify(dropIndexArr)){
            if (dropPosition === -1) {
              (dropArr as any).splice(dropIndex,0,dragObj) //在目标节点前插入
            } else {
              (dropArr as any).splice(dropIndex+1,0,dragObj) //在目标节点后插入
            }
            
          }else{
            console.log('跨级拖拽合并不可，例如二级向三级下追加')
            // return false
            // if()
            if (dropPosition === -1) {
              (dropArr as any).splice(dropIndex,0,dragObj) //在目标节点前插入
            } else {
              (dropArr as any).splice(dropIndex+1,0,dragObj) //在目标节点后插入
            }
          }
        }
        
      }
      //重新刷新数据
      treeData.value=data;
    },
    onCancel() {
      console.log('Cancel');
    },
    class: 'test',
  });


}
</script>
<style scoped>
.treeContainer{
    width:500px;
    min-height:400px;
    /* border:1px solid red; */
    margin:20px auto; 
}
.tooltip{
  width:100px;
  height:50px;
  background:#409eff;
  line-height:50px;
  text-align: center;
  border-radius: 10px;
  position:absolute;
}
</style>